package com.elasticcloudservice.predict;

import com.filetool.util.CloneUtils;

import java.util.*;

/**
 * @Author: Chy
 * @Description: 迭代
 * @Date: Created at 17:52 2018/4/12
 */
public class Generation {
    int popSize;  //种群规模
    int dominants ;  //优势个体数量
    double domRate;
    double mutationRate;  //变异率

    private List<Chromosome> population = new ArrayList<>();

    private Chromosome bestChromosome = new Chromosome();

    public Generation(int popSize, int dominants, double domRate, double mutationRate) {
        this.popSize = popSize;
        this.dominants = dominants;
        this.domRate = domRate;
        this.mutationRate = mutationRate;
    }

    public List<Chromosome> getPopulation() {
        return population;
    }

    public void setPopulation(List<Chromosome> population) {
        this.population = population;
    }

    public Chromosome getBestChromosome() {
        return bestChromosome;
    }

    public void setBestChromosome(Chromosome bestChromosome) {
        this.bestChromosome = bestChromosome;
    }


    /**
     * @Author: Chy
     * @Param:
     * @Description: 初始化种群 FF算法装箱
     * @Date: 18:51 2018/4/12
     */
    public void init(int popSize, List<VirtualMachine> vmList, List<ElasticCloudServer> allEcs) {

        Random random = new Random();
        for (int i = 0; i < popSize; i++) {
            Chromosome chromosome = new Chromosome();

            //FF装箱
            int randomIndex =  random.nextInt(allEcs.size());
            ElasticCloudServer randomEcs = CloneUtils.clone(allEcs.get(randomIndex));

            List<ElasticCloudServer> ecsList = new ArrayList<>();
            Collections.shuffle(vmList);
            ecsList.add(randomEcs);

            for (int j = 0; j < vmList.size(); j++) {
                VirtualMachine vm = vmList.get(j);
                boolean isContained = false;

                for (ElasticCloudServer ecs : ecsList){
                    if (vm.getCPUs() <= ecs.getCPUs() && vm.getMems() <= ecs.getMems()) {
                        ecs.add(vm);
                        isContained = true;
                        break;
                    }
                }
                if (!isContained) {
                    ElasticCloudServer ecs = CloneUtils.clone(allEcs.get(random.nextInt(allEcs.size())));
                    ecs.add(vm);
                    ecsList.add(ecs);
                }
            }

            for (ElasticCloudServer ecs : ecsList)
                ecs.setUseRate(ecs.getUseRate());
            chromosome.setEcsList(ecsList);

            population.add(chromosome);
        }
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 计算个体适应度
     * @Date: 20:06 2018/4/12
     */
    public void countFitness(List<Chromosome> population) {
        for (Chromosome chromosome : population){
            //适应度函数
            double value1 = .0;
            double value2 = .0;
            double fitness = .0;

            double totalEcsCPU = .0;  //服务器总CPU
            double totalEcsMem = .0;  //服务器总MEM
            double ecsLeftCPU = .0;  //服务器剩余CPU
            double ecsLeftMem = .0;  //服务器剩余MEM
            for (ElasticCloudServer ecs : chromosome.getEcsList()){
                ecsLeftCPU += ecs.getCPUs();
                ecsLeftMem += ecs.getMems();

                totalEcsCPU += ecs.getOriginCPU();
                totalEcsMem += ecs.getOriginMem();
            }

            value1 = ecsLeftCPU / totalEcsCPU;
            value2 = ecsLeftMem / totalEcsMem;
            fitness = (value1 + value2) / 2;
            chromosome.setFitness(fitness);
        }
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 选择
     * @Date: 20:30 2018/4/12
     */
    public void sort() {
        //根据适应度从小到大对个体进行排序
        Collections.sort(population, new ChromosomeComparator());
        bestChromosome = population.get(0);
    }

   /**
    * @Author: Chy
    * @Param:
    * @Description: 产生下一代
    * @Date: 21:16 2018/4/12
    */
    public Generation nextGeneration(int popSize, int dominants, double domRate, double mutationRate, List<ElasticCloudServer> allEcs) {
        Generation nextGeneration = new Generation(popSize, dominants, domRate, mutationRate);
        List<Chromosome> nextPopulation = nextGeneration.getPopulation();
        Random random = new Random();

//        if (domRate > 0 && dominants > 0){
//            //优势个体的集合
//            List<Chromosome> dominantChromosome = new ArrayList<>();
//
//            Iterator<Chromosome> iterator = population.iterator();
//            dominantChromosome.add(iterator.next());
//
//            for (int harem = (int) (popSize * domRate); harem > 0; harem--) {
//                if (dominantChromosome.size() > dominants || nextPopulation.size() >= popSize)
//                    break;
//
//                Chromosome nextChromosome = iterator.next();
//                //将所有优势个体交配得到的子代加入到种群中（即实现了优势个体的基因较大概率的保留下去）
//                for (Chromosome chromosome : dominantChromosome) {
//                    Chromosome child = Crossover.mate(chromosome, nextChromosome, allEcs); //交叉
//                    if (random.nextDouble() <= mutationRate)  //变异
//                        Crossover.mutate(child, allEcs);
//                    nextPopulation.add(child);
//                }
//
//                dominantChromosome.add(nextChromosome);
//            }
//        }

        //random mating until population is filled
        while (nextPopulation.size() < popSize) {
            //put the tree set into an array list for random access, NOTE: slows things down a little
            List<Chromosome> pop = new ArrayList<>(population);

            int index1 = random.nextInt(pop.size());
            int index2 = random.nextInt(pop.size());
            //ensure a solution doesn't mate with itself
            if (index1 == index2)
                index2 = (index2 == 0) ? (index2 + 1) : (index2 - 1);

            Chromosome child = Crossover.mate(pop.get(index1), pop.get(index2), allEcs);
            if (random.nextDouble() <= mutationRate)
                Crossover.mutate(child, allEcs);
            nextPopulation.add(child);
        }

        //check the currentBest in this next population and if it is better than overallBest, replace
        countFitness(nextPopulation);
        Collections.sort(nextPopulation, new ChromosomeComparator());
        Chromosome currentBest = nextPopulation.get(0);
//        if (currentBest.getFitness() <= bestChromosome.getFitness())
            bestChromosome = currentBest;


        return nextGeneration;
    }

    /**
     * @Author: Chy
     * @Description: 个体比较器，依据适应度排序
     * @Date: 23:34 2018/3/10
     */
    private static class ChromosomeComparator implements Comparator<Chromosome> {
        @Override
        public int compare(Chromosome c1, Chromosome c2) {
//            return Double.compare(c1.getFitness(), c2.getFitness());
            if(c1.getFitness() > c2.getFitness())
                return 1;
            if(c1.getFitness() < c2.getFitness())
                return -1;
            return 0;
        }
    }


}